package com.miyake.server.dsaccess;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.jdo.PersistenceManager;

import com.miyake.client.datamanage.Data;
import com.miyake.client.datamanage.NodeProperty;
import com.miyake.client.datamanage.Paper;
import com.miyake.client.datamanage.Parameter;
import com.miyake.client.datamanage.PlotData;
import com.miyake.client.datamanage.PlotSetting;
import com.miyake.client.datamanage.Text;
import com.miyake.server.DsAlias;
import com.miyake.server.DsData;
import com.miyake.server.DsImage;
import com.miyake.server.DsNode;
import com.miyake.server.DsPaper;
import com.miyake.server.DsParameter;
import com.miyake.server.DsPlot;
import com.miyake.server.DsProject;
import com.miyake.server.DsTable;
import com.miyake.server.PMF;
import com.miyake.server.DsText;
import com.miyake.server.user.UserManager;

/**
 * @author a1199022
 *
 */
public class NodeManager {
	private static NodeManager instance;
	
	private NodeManager() {
	}
	
	public static NodeManager getInstance() {
		if (instance == null) {
			instance = new NodeManager();
		}
		return instance;
	}
	
	public String createRootNode(String name, Integer order) {
		final DsProject project = new DsProject();
		CreateSomething<DsProject> create = new CreateSomething<DsProject>("null", name, NodeProperty.ROOTNODE, order) {
			@Override
			DsProject getObject() {
				return project;
			}

			@Override
			void connect(DsProject object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
		};
		return create.getKey();
		
//		return createNode("null", name, NodeProperty.ROOTNODE).getEncodedKey();
	}
	
//	
	public DsNode createNode(String parentKey, String name, Integer type, final Integer order) {
//		List<DsNode> list = this.getSubNodes(parentKey);
		final DsNode newNode = new CreateNode() {
			@Override
			protected void update(DsNode dsNode) {
				dsNode.setSecurityLevel(0);
				dsNode.setOwner(UserManager.getInstance().getMyself().getEncodedKey());
			}
			
		}.create(parentKey, name, type);
		
		if (!parentKey.equals("null")) {
			new GetNode(){
				@Override
				protected void update(DsNode node) {
					node.addSubNode(newNode.getEncodedKey(), order);
				} 
			}.getByKey(parentKey);
		}
		return newNode;
	}
	
	public List<DsNode> getRootNodes(String userKey) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		List<DsNode> list = null;
		try {
			String query = "select from " + DsNode.class.getName();
			query += " where owner == " + "\'" + userKey + "\'";
			query += " && type == " + String.valueOf(NodeProperty.ROOTNODE);
			list = (List<DsNode>) pm.newQuery(query).execute();	
			for (DsNode n : list) {
				System.out.println(n.getName());
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
		return list;
	}
	
	public static List<NodeProperty> toRemoteObjectDsNodes(List<DsNode> nodes) {
		List<NodeProperty> ret = new ArrayList<NodeProperty>();
		for (DsNode node : nodes) {
			ret.add(node.toRemoteObject());
		}
		return ret;
	}
	
	public void deleteNode(final String key) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		
		try {
			// Removing the node
			DsNode node = pm.getObjectById(DsNode.class, key);
						
			String substKey = node.getSubstKey();
			
			// Removing the substantial object
			switch (node.getType()) {
			case NodeProperty.ALIAS:
				DsAlias alias = pm.getObjectById(DsAlias.class, substKey);
				pm.deletePersistent(alias);
				break;

			case NodeProperty.PLOT:
				DsPlot plot = pm.getObjectById(DsPlot.class, substKey);
				pm.deletePersistent(plot);
				break;
			case NodeProperty.FOLDER:
				
				break;
			case NodeProperty.DATA:
				DsData data = pm.getObjectById(DsData.class, substKey);
				pm.deletePersistent(data);
				break;
			case NodeProperty.IMAGE:
				DsImage image = pm.getObjectById(DsImage.class, substKey);
				pm.deletePersistent(image);
				break;				
			case NodeProperty.PAPER:
				DsPaper paper = pm.getObjectById(DsPaper.class, substKey);
				pm.deletePersistent(paper);
				break;
			case NodeProperty.TABLE:
				DsTable table = pm.getObjectById(DsTable.class, substKey);
				pm.deletePersistent(table);
			case NodeProperty.ROOTNODE:
				DsProject project = pm.getObjectById(DsProject.class, substKey);
				pm.deletePersistent(project);
			default:
				break;
			};

			// Removing children
			List<String> list = node.getSubNodes();
			for (String s : list) {
				deleteNode(s);
			}
			
			if (!node.getParentKey().equals("null")) {
				new GetNode() {
					@Override
					protected void update(DsNode node) {
						node.getSubNodes().remove(key);
					}
				}.getByKey(node.getParentKey());
			}
			pm.deletePersistent(node);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
	}
		
	public String createData(String name, String parentKey, final DsData data, Integer order) {
		CreateSomething<DsData> create = new CreateSomething<DsData>(parentKey, name, NodeProperty.DATA, order) {
			@Override
			DsData getObject() {
				return data;
			}

			@Override
			void connect(DsData object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
		};
		return create.getKey();
	}
	
	public DsData getData(String nodeKey) {
		GetSomething<DsData> get = new GetSomething<DsData>(nodeKey, DsData.class.getName());
		return get.getObject();
	}
	
 	public String createPlot(String name, String parentKey, final DsPlot plot, Integer order) { 
		
 		CreateSomething<DsPlot> create = new CreateSomething<DsPlot>(parentKey, name, NodeProperty.PLOT, order) {
			@Override
			DsPlot getObject() {
				return plot;
			}

			@Override
			void connect(DsPlot object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}

	public String createText(String name, String parentKey, final DsText data, Integer order) {
 		CreateSomething<DsText> create = new CreateSomething<DsText>(parentKey, name, NodeProperty.TEXT, order) {
			@Override
			DsText getObject() {
				return data;
			}

			@Override
			void connect(DsText object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}
	
 	public DsNode getNode(String key) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		DsNode node = null;
		try {
			node = pm.getObjectById(DsNode.class, key);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();		
		}
		return node;
 	}
 	
	public DsPlot getPlot(String nodeKey) {
		GetSomething<DsPlot> get = new GetSomething<DsPlot>(nodeKey, DsPlot.class.getName());		
		return get.getObject();
	}

	public void updatePlot(final String nodeKey, final PlotSetting scale) {
		new GetSomething<DsPlot>(nodeKey, DsPlot.class.getName()) {
			@Override
			void update(DsPlot object) {
				if (scale.getXmin()!= null) {
					object.setXmin(scale.getXmin());
				}
				if (scale.getXmax() != null) {
					object.setXmax(scale.getXmax());
				}
				if (scale.getYmin() != null) {
					object.setYmin(scale.getYmin());
				}
				if (scale.getYmax() != null) {
					object.setYmax(scale.getYmax());
				}
				if (scale.getPlotType() != null) {
					object.setType(scale.getPlotType());
				}
				if (scale.getDefaultScale() != null && scale.getDefaultScale() == true) {
					object.setDefaultScale();
				}
			}
		};
	}

	public DsText getText(String nodeKey) {
		GetSomething<DsText> get = new GetSomething<DsText>(nodeKey, DsText.class.getName());		
		return get.getObject();

	}

	public void updateText(String nodeKey, final Text text) {
		int len = text.getText().length();
		if (len > 10) {
			len = 10;
		}
		this.updateNode(nodeKey, text.getText().substring(0, len) + "...", null, null);
		GetSomething<DsText> get = new GetSomething<DsText>(nodeKey, DsText.class.getName()){
			@Override
			void update(DsText object) {
				if (text.getText() != null) {
					object.setText(text.getText());
				}
				if (text.getBold() != null) {
					object.setBold(text.getBold());
				}
				if (text.getAlign() != null) {
					object.setAlignment(text.getAlign());
				}
				if (text.getFontsize() != null) {
					object.setFontsize(text.getFontsize());
				}
			}
		};
		
	}

	public void updateNode(String key, final String name, final String parentKey, final Integer order) {
		new GetNode() {
			@Override
			protected void update(DsNode node) {
				if (name != null) {
					node.setName(name);
				}
				if (parentKey != null) {
					node.setParentKey(parentKey);
				}
				if (order != null) {

				}
			}
			
		}.getByKey(key);

	}
	
	public void moveNodeUp(final String key) {
		new GetNode() {
			@Override
			protected void update(DsNode node) {
				int i = 0;
				for (String s : node.getSubNodes()) {
					if (s.equals(key)) {
						node.upSubNode(s);
					}
				}
			}
		}.getByKey(getNode(key).getParentKey());
	}

	public void moveNodeDown(final String key) {
		new GetNode() {
			@Override
			protected void update(DsNode node) {
				int i = 0;
				for (String s : node.getSubNodes()) {
					if (s.equals(key)) {
						node.downSubNode(s);
					}
				}
			}
		}.getByKey(getNode(key).getParentKey());
	}

	public String createTable(String name, String parentKey, Integer order) {
		final DsTable data = new DsTable();
		CreateSomething<DsTable> create = new CreateSomething<DsTable>(parentKey, name, NodeProperty.TABLE, order) {
			@Override
			DsTable getObject() {
				return data;
			}

			@Override
			void connect(DsTable object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}

	public DsTable getTable(String nodeKey) {
		GetSomething<DsTable> get = new GetSomething<DsTable>(nodeKey, DsTable.class.getName());		
		return get.getObject();
	}

	public String createImage(String nodeKey, String name, final DsImage image, Integer order) {
 		CreateSomething<DsImage> create = new CreateSomething<DsImage>(nodeKey, name, NodeProperty.IMAGE, order) {
			@Override
			DsImage getObject() {
				return image;
			}

			@Override
			void connect(DsImage object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}

	public DsImage getImage(String nodeKey) {
		GetSomething<DsImage> get = new GetSomething<DsImage>(nodeKey, DsImage.class.getName());
		return get.getObject();
	}

	public String createPaper(final String name, String nodeKey, Integer order) {
 		CreateSomething<DsPaper> create = new CreateSomething<DsPaper>(nodeKey, name, NodeProperty.PAPER, order) {
			@Override
			DsPaper getObject() {
				DsPaper paper = new DsPaper();
				paper.setName(name);
				return paper;
			}

			@Override
			void connect(DsPaper object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}

	public DsPaper getPaper(String nodeKey) {
		GetSomething<DsPaper> get = new GetSomething<DsPaper>(nodeKey, DsPaper.class.getName());
		return get.getObject();
	}

	public List<String> getPlotList(String nodeKey) {
		FindSomething<DsPlot> list = new FindSomething<DsPlot>(nodeKey, DsPlot.class.getName());
		List<String> ret = new ArrayList<String>();
		for (DsPlot plot : list.getList()) {
			ret.add(plot.getNodeKey());
		}
		return ret;
	}

	public String createAlias(String nodeKey, final DsAlias alias, Integer order) {
		DsNode node = this.getNode(alias.getAliasKey());
 		CreateSomething<DsAlias> create = new CreateSomething<DsAlias>(nodeKey, node.getName(), NodeProperty.ALIAS, order) {
			@Override
			DsAlias getObject() {
				return alias;
			}

			@Override
			void connect(DsAlias object, DsNode node) {
				object.setNodeKey(node.getEncodedKey());
				node.setSubstKey(object.getEncodedKey());
			}
 		};
 		return create.getKey();
	}

	public DsAlias getAlias(String nodeKey) {
		GetSomething<DsAlias> get = new GetSomething<DsAlias>(nodeKey, DsAlias.class.getName());
		return get.getObject();
	}

	public List<String> getImageList(String nodeKey) {
		FindSomething<DsImage> list = new FindSomething<DsImage>(nodeKey, DsImage.class.getName());
		List<String> ret = new ArrayList<String>();
		for (DsImage obj : list.getList()) {
			ret.add(obj.getNodeKey());
		}
		return ret;
	}

	public void updateData(String nodeKey, final Data data) {
		new GetSomething<DsData>(nodeKey, DsData.class.getName()) {
			@Override
			void update(DsData object) {
				if (data.getTitleRow() != null) {
					object.setTitleRow(data.getTitleRow());
				}
				if (data.getFilePluginKey() != null) {
					object.setFilePluginKey(data.getFilePluginKey());
				}
				if (data.getAnalysisPluginKey() != null) {
					object.setAnalysisPluginKey(data.getAnalysisPluginKey());
				}
				if (data.isParsed() != null) {
					object.setParsed(data.isParsed());
				}
				if (data.getParameters().size() > 0) {
					object.getParameters().clear();
					for (Parameter p : data.getParameters()) {
						object.getParameters().add(createParameter(p.getId(), p.getTitle(), p.getValue(), p.getUnit()));
					}
				}
				
				if (data.getTextData().size() > 0) {
					object.getBinData().clear();
					Iterator<String> it = data.getTitles().iterator();
					for (String[] arr : data.getTextData()) {
						String title = "";
						if (it.hasNext()) {
							title = it.next();
						}
						object.addData(arr, title);
					}
				}
				if (data.getXmax() != null) {
					object.setXmax(data.getXmax());
				}
				if (data.getXmin() != null) {
					object.setXmin(data.getXmin());
				}
			}
		};

	}

	public void setSecurityLevel(String nodeKey, final Integer level) {
		new GetNode() {
			@Override
			protected void update(DsNode node) {
				node.setSecurityLevel(level);
			}
		}.getByKey(nodeKey);
	}

	public List<Paper> getPaperList(String communityKey) {
		List<Paper> ret = new ArrayList<Paper>();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			String query = "select from " + DsPaper.class.getName();
			List<DsPaper> list = (List<DsPaper>) pm.newQuery(query).execute();
			for (DsPaper t : list) {
				ret.add(t.toRemoteObject());
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
		return ret;
	}

	public List<NodeProperty> getOpenDataList(String userKey) {
		List<NodeProperty> ret = new ArrayList<NodeProperty>();
		PersistenceManager pm = PMF.get().getPersistenceManager();
		try {
			String query = "select from " + DsNode.class.getName();
			query += " where owner == " + "\'" + userKey + "\'";
			query += " && securityLevel > 0";
	
			List<DsNode> list = (List<DsNode>) pm.newQuery(query).execute();
			for (DsNode t : list) {
				ret.add(t.toRemoteObject());
			}
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
		return ret;
	}

	public void renameNode(String key, final String name) {
		new GetNode() {
			@Override
			protected void update(DsNode node) {
				node.setName(name);
			}
		}.getByKey(key);
	}

	public String createParameter(String id, String title, String value, String unit) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		DsParameter p = null;
		try {
			p = new DsParameter(id, title, value, unit);
			pm.makePersistent(p);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
		return p.getEncodedKey();
	}
	
	public DsParameter getParameter(String key) {
		PersistenceManager pm = PMF.get().getPersistenceManager();
		DsParameter ret = null;
		try {
			ret = pm.getObjectById(DsParameter.class, key);
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		finally {
			pm.close();
		}
		return ret;
	}

	public void addPlotOverlay(String nodeKey, final String overlayNodeKey) {
		new GetSomething<DsPlot>(nodeKey, DsPlot.class.getName()) {
			@Override
			void update(DsPlot object) {
				if(!object.getOverlays().contains(overlayNodeKey)) {
					object.getOverlays().add(overlayNodeKey);
				}
			}
		};
	}

	public void getSubList(DsNode node, Integer type, List<NodeProperty> list) {
		
		for (String k : node.getSubNodes()) {
			DsNode n = getNode(k);

			if ( ((int)n.getType() & type.intValue()) > 0) {
				list.add(n.toRemoteObject());
			}
			getSubList(n, type, list);
		}
	}

	
	public List<NodeProperty> getSpecifiedSubNodes(String key, Integer type) {
		List<NodeProperty> ret = new ArrayList<NodeProperty>();
		getSubList(this.getNode(key), type, ret);
		return ret;
	}

	
	public void deletePlotOverlay(String nodeKey, final String overlayNodeKey) {
		new GetSomething<DsPlot>(nodeKey, DsPlot.class.getName()) {
			@Override
			void update(DsPlot object) {
				object.getOverlays().remove(overlayNodeKey);
			}
		};
	}

	private DsNode findRoot(String subNode) {
		DsNode node = getNode(subNode);
		if (node.getParent() != null) {
			return findRoot(node.getParent().getEncodedKey());
		}
		return node;
	}
	
	public NodeProperty getRootNode(String subNode) {
		return findRoot(subNode).toRemoteObject();
	}
}
